package com.informa.java.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.collections15.SetUtils;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.springframework.util.Assert;


/**
 * 
 * Common collection utilities
 * 
 * 
 * 
 */

public class CollectionUtils {

	/**
	 * 
	 * Create set containing objects from collection
	 * 
	 * @param <E>
	 *            Type of objects
	 * 
	 * @param collection
	 *            Collection of objects
	 * 
	 * @return Set containing objects from collection
	 * 
	 */

	public static <E> Set<E> asSet(Collection<E> collection) {
		
		Assert.notNull(collection, "Must provide a collection");	
		
		Set<E> set = new HashSet<E>();
		set.addAll(collection);
		
		return set;

	}

	public static <E,F extends E> Set<E> asSet(F ... objects) {
		return new HashSet<E>(Arrays.asList(objects));
	}
	
	@SuppressWarnings("unchecked")
	public static <E,F extends E> Set<E> asImmutableSet(F ... objects) {
		return (Set<E>) SetUtils.unmodifiableSet(asSet(objects));
	}
	
	/**
	 * 
	 * Create sorted set containing objects from collection
	 * 
	 * @param <E>
	 *            Type of objects
	 * 
	 * @param collection
	 *            Collection of objects
	 * 
	 * @return sorted set containing the objects from the collection
	 * 
	 */

	public static <E> SortedSet<E> asSortedSet(Collection<E> collection) {		
		
		Assert.notNull(collection, "Must provide a collection");	
		
		SortedSet<E> set = new TreeSet<E>();
		set.addAll(collection);	
		
		return set;

	}
	public static <E> Set<E> asOrderedSet(Collection<E> collection) {		
		
		Assert.notNull(collection, "Must provide a collection");	
		
		Set<E> set = new ListOrderedSet<E>();
		set.addAll(collection);
		
		return set;
		
	}
	public static <E> Set<E> asOrderedSet(E ... objects) {		
		
		Assert.notNull(objects, "Must provide a collection");	
		
		Set<E> set = new ListOrderedSet<E>();
		set.addAll(asList(objects));	
		
		return set;
		
	}
	
	public static <E> SortedSet<E> asSortedSet(E ... items) {		
		
		Assert.notNull(items, "Must provide an array");	
		
		SortedSet<E> set = new TreeSet<E>();
		set.addAll(Arrays.asList(items));	
		
		return set;

	}

	public static <E,F, A extends E, B extends F> Map<E,F> asMap(A key, B value) {
		
		Assert.notNull(key, "Must provide a key");
		
		Map<E,F> map = new HashMap<E, F>();
		map.put(key, value);
		
		return map;
	}

	public static <E, F extends E> List<E> asList(Collection<F> set) {
		
		Assert.notNull(set, "Must provide a collection");
		
		List<E> list = new ArrayList<E>();
		list.addAll(set);
		
		return list;
	}

	public static <A,B extends A> List<A> asList(B ... objects) {
		
		Assert.notNull(objects, "Must provide an array of objects");
		
		List<A> list = new ArrayList<A>();
		
		for(B object : objects) {
			list.add(object);
		}
		
		return list;
	}

	public static <A, B extends A> List<A> asList(Iterator<B> iterator) {
		
		Assert.notNull(iterator, "Must provide an iterator");
				
		List<A> list = new ArrayList<A>();
		
		while(iterator.hasNext()) {
			list.add(iterator.next());
		}

		return list;
	}
	
	public static List<String> commaDelimitedStringsToList(String commaSeparatedNames) {
		
		Assert.notNull(commaSeparatedNames, "Name is Null.");
		
		List<String> trimmmedNames = new ArrayList<String>();
		
		List<String> names = asList(commaSeparatedNames.split(","));
		for(String name : names) {
			trimmmedNames.add(name.trim());
		}
		
		return trimmmedNames;
	}
	
	public static <A> List<A> EMPTY_LIST() {
		return new ArrayList<A>();
	}
	
	public static <A> Set<A> EMPTY_SET() {
		return new HashSet<A>();
	}
	
	public static <A, B extends A> A lastElement(List<B> list) {
		return list.get(list.size() - 1);
	}
	
	public static <A, B extends A> A firstElement(List<B> list) {
		return list.get(0);
	}

}
